package mvc.core;

import java.io.File;
import java.io.FileFilter;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Set;

import mvc.core.log.Logger;

/**
 * classpath类扫描器
 * 
 * @author liuzy
 * @version 2015年5月13日
 */
public class ClassScanner {
	
	private Logger logger = Logger.getLogger(ClassScanner.class);
	
	public static String CLASSPATH = "com";

	/** 包中所有的Java类 */
	private Set<Class<?>> allClasses = new LinkedHashSet<Class<?>>();
	
	private static ClassScanner scanner = new ClassScanner();
	
	private ClassScanner() {}
	
	/**
	 * 获取包中所有的类
	 */
	public static Set<Class<?>> scanner() {
		return scanner.scanner(CLASSPATH);
	}
	
	/**
	 * 获取包中所有的类
	 */
	private Set<Class<?>> scanner(String classpath) {
		try {
			logger.debug("扫描包:"+classpath);
			// 获取包的名字 并进行替换
			String packageDirName = CLASSPATH.replace('.', '/');
			// 定义一个枚举的集合 并进行循环来处理这个目录下的things
			Enumeration<URL> dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
			// 循环迭代
			while (dirs.hasMoreElements()) {
				// 获取下一个元素
				URL url = dirs.nextElement();
				// 获取包的物理路径
				String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
				// 以文件的方式扫描整个包下的文件 并添加到集合中
				scannerDir(CLASSPATH, filePath);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return allClasses;
	}

	/**
	 * 递归扫描文件夹
	 */
	private void scannerDir(String packageName, String packagePath) {
		// 获取此包的目录 建立一个File
		File dir = new File(packagePath);
		// 如果不存在或者 也不是目录就直接返回
		if (!dir.exists() || !dir.isDirectory()) {
			// log.warn("用户定义包名 " + packageName + " 下没有任何文件");
			return;
		}
		// 如果存在 就获取包下的所有文件 包括目录
		File[] dirfiles = dir.listFiles(new FileFilter() {
			// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
			public boolean accept(File file) {
				return file.isDirectory() || (file.getName().endsWith(".class") && file.getName().indexOf("$") == -1);
			}
		});
		// 循环所有文件
		for (File file : dirfiles) {
			// 如果是目录 则继续扫描
			if (file.isDirectory()) {
				scannerDir(packageName + "." + file.getName(), file.getAbsolutePath());
			} else {
				// 如果是java类文件 去掉后面的.class 只留下类名
				String className = file.getName().substring(0, file.getName().length() - 6);
				try {
					// 用forName会触发static方法，没有使用classLoader的load干净
					// Class<?> clazz = Class.forName(packageName + '.' + className);
					Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className);
					allClasses.add(clazz);
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				}
			}
		}
	}

}
